/*
 * Copyright (c) 2005, 2014 vacoor
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 */
package org.vacoor.nothing.security.shiro.util;

import com.google.common.collect.Sets;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.util.CollectionUtils;
import org.vacoor.nothing.security.entity.Module;
import org.vacoor.nothing.security.entity.Permission;
import org.vacoor.nothing.security.entity.Role;

import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

/**
 */
public class SubjectContext2 {
    private static final String ROLE_LIST_SESSION_KEY = SubjectContext2.class.getName() + "_ROLE_LIST";
    private static final String PERM_LIST_SESSION_KEY = SubjectContext2.class.getName() + "_PERM_LIST";
    private static final String MOD_LIST_SESSION_KEY = SubjectContext2.class.getName() + "_MOD_LIST";
    private static final String MOD_TREE_SESSION_KEY = SubjectContext2.class.getName() + "_MOD_TREE";
    private static final String ROOT_MOD_ID = "root";

    private final Subject subject;

    public static SubjectContext2 getContext() {
        return new SubjectContext2(SecurityUtils.getSubject());
    }

    private SubjectContext2(Subject subject) {
        this.subject = subject;
    }

    public Object getPrincipal() {
        return subject.getPrincipal();
    }

    public PrincipalCollection getPrincipals() {
        return subject.getPrincipals();
    }

    public List<Role> getRoles() {
        Session session = subject.getSession(false);
        if (session == null) {
            return Collections.<Role>emptyList();
        }

        return (List<Role>) session.getAttribute(ROLE_LIST_SESSION_KEY);
    }

    public List<Permission> getPermissions() {
        Session session = subject.getSession(false);
        if (session == null) {
            return Collections.<Permission>emptyList();
        }

        return (List<Permission>) session.getAttribute(PERM_LIST_SESSION_KEY);
    }

    public List<Module> getModules() {
        Session session = subject.getSession(false);
        if (session == null) {
            return Collections.<Module>emptyList();
        }

        return (List<Module>) session.getAttribute(MOD_LIST_SESSION_KEY);
    }

    public Module getModuleTree() {
        Session session = subject.getSession(false);
        if (session == null) {
            return null;
        }

        Module root = (Module) session.getAttribute(MOD_TREE_SESSION_KEY);
        if (root != null) {
            return root;
        }

        List<Module> modules = getModules();
        if (CollectionUtils.isEmpty(modules)) {
            return null;
        }

        String rootId = ROOT_MOD_ID;
        root = new Module();
        root.setId(rootId);

        buildModuleTree(root, Sets.newHashSet(modules));
        for (Module module : modules) {
            String mid = module.getId();
            if (rootId == mid || (rootId != null && rootId.equals(mid))) {
                module.setChildren(root.getChildren());
                root = module;
                break;
            }
        }
        session.setAttribute(MOD_TREE_SESSION_KEY, root);
        return root;
    }

    protected static void buildModuleTree(Module root, Set<Module> modules) {
        if (root == null || modules == null || modules.size() < 1) {
            return;
        }

        String rid = root.getId();
        Set<Module> parents = Sets.newHashSet();
        Iterator<Module> it = modules.iterator();
        while (it.hasNext()) {
            Module module = it.next();
            Module parent = module.getParent();
            String pid = parent != null ? parent.getId() : null;

            if (rid == pid || (rid != null && rid.equals(pid))) {
                Set<Module> children = root.getChildren();
                if (children == null) {
                    children = Sets.newHashSet();
                    root.setChildren(children);
                }
                children.add(module);
                it.remove();

                parents.add(module);
            }
        }
        for (Module parent : parents) {
            buildModuleTree(parent, modules);
        }
    }
}
